package com.xlf.service.judge.service.impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xlf.common.core.constant.RedisConst;
import com.xlf.common.core.exception.BizException;
import com.xlf.common.core.util.StringUtils;
import com.xlf.common.redis.service.RedisCache;
import com.xlf.pojo.base.Judge;
import com.xlf.pojo.base.Problem;
import com.xlf.pojo.dto.ContestRecordDTO;
import com.xlf.pojo.dto.RunResult;
import com.xlf.pojo.constant.Constants;
import com.xlf.service.judge.exception.CompileError;
import com.xlf.service.judge.io.LanguageConfigLoader;
import com.xlf.service.judge.mapper.JudgeCaseMapper;
import com.xlf.service.judge.mapper.JudgeMapper;
import com.xlf.service.judge.mapper.ProblemMapper;
import com.xlf.service.judge.pojo.*;
import com.xlf.service.judge.service.JudgeService;
import com.xlf.service.judge.strategy.judgeMachine.content.JudgeMachineContent;
import com.xlf.service.judge.strategy.judgeProblemMode.content.JudgeProblemModeContent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xlf.pojo.dto.TestJudgeReq;

import javax.annotation.Resource;
import java.util.function.Consumer;


/**
 * @Author xlf 2734029211@qq.com
 * @Date 2023/9/19 16:11
 * @Description: TODO
 * @Version 1.0
 */
@Service
@Slf4j
public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge>
        implements JudgeService {


    @Autowired
    JudgeMachineContent judgeMachineContent;

    @Autowired
    JudgeProblemModeContent judgeProblemModeContent;

    @Resource
    JudgeMapper judgeMapper;

    @Resource
    ProblemMapper problemMapper;

    @Resource
    JudgeCaseMapper judgeCaseMapper;

    @Autowired
    RedisCache redisCache;

    @Override
    public void judge(String judgeId) {

        Judge judge = Assert.notNull(judgeMapper.selectById(judgeId), () -> new BizException("判题纪录为空！"));

        Problem problem = Assert.notNull(problemMapper.selectById(judge.getPid()), () -> new BizException("题目为空！"));

        //TODO 1.标志该判题过程进入编译阶段
        judge.setStatus(Constants.Judge.STATUS_COMPILING.getStatus());
        lambdaUpdate().eq(Judge::getId,judge.getId())
                .set(Judge::getStatus,Constants.Judge.STATUS_COMPILING.getStatus())
                .update();

        JudgeResult judgeResult = null;
        try {

            //TODO 2.运行题目，得到使用测试用例的运行结果，IO得分为测试用例得分。
            judgeResult = judgeMachineContent.execute(problem, judge, new Consumer<Judge>() {
                @Override
                public void accept(Judge judge) {
                    judge.setStatus(Constants.Judge.STATUS_JUDGING.getStatus());
                    lambdaUpdate().eq(Judge::getId,judge.getId())
                            .set(Judge::getStatus,Constants.Judge.STATUS_JUDGING.getStatus())
                            .update();
                }
            });

            //TODO 3.判题，计算得分。
            judgeProblemModeContent.Judge(problem,judge,judgeResult.getJudgeCase());

        } catch (CompileError e) {
            //编译失败
            judge.setStatus(Constants.Judge.STATUS_COMPILE_ERROR.getStatus());
            judge.setErrorMessage(e.getStderr());
        } catch (Exception e){
            e.printStackTrace();
            log.error("判题id:" + judgeId + "判题时系统出现异常，原因：" + e.getMessage());
            //系统错误
            judge.setStatus(Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());
        }


        //TODO 4.更新判题结果
        updateById(judge);
        if(judgeResult != null && StringUtils.isNotEmpty(judgeResult.getJudgeCase())){
            judgeResult.getJudgeCase().forEach(item -> {
                item.setInputData(null);
                item.setOutputData(null);
                item.setUserOutput(null);
                judgeCaseMapper.insert(item);
            });
        }
    }

    @Override
    public void testJudge(TestJudgeReq testJudgeReq) {

        RunResult runResult = new RunResult();
        try {

            LanguageConfigLoader languageConfigLoader = judgeMachineContent.getLanguageConfigLoader();
            // c和c++为一倍时间和空间，其它语言为2倍时间和空间
            LanguageConfig languageConfig = languageConfigLoader.getLanguageConfigByName(testJudgeReq.getLanguage());
            if(languageConfig == null)
                throw new BizException("不支持[" + testJudgeReq.getLanguage() + "]语言");
//            if (languageConfig.getSrcName() == null
//                    || (!languageConfig.getSrcName().endsWith(".c")
//                    && !languageConfig.getSrcName().endsWith(".cpp"))) {
//                testJudgeReq.setTimeLimit(testJudgeReq.getTimeLimit() * 2);
//                testJudgeReq.setMemoryLimit(testJudgeReq.getMemoryLimit() * 2);
//            }

            runResult = judgeMachineContent.testJudge(testJudgeReq);
            if(testJudgeReq.getExpectedOutput() != null){
                Integer isRemoveEndBlank = testJudgeReq.getIsRemoveEndBlank();
                if(isRemoveEndBlank == null)
                    isRemoveEndBlank = 1;
                //
                if(runResult.getStatus().equals(Constants.Judge.STATUS_ACCEPTED.getStatus())){
                    Integer status = judgeProblemModeContent.testJudge(runResult.getFile(), isRemoveEndBlank,testJudgeReq.getExpectedOutput());
                    runResult.setStatus(status);
                }
            }

        } catch (CompileError e) {
            //编译失败
            runResult.setStatus(Constants.Judge.STATUS_COMPILE_ERROR.getStatus());
            runResult.setMsg(e.getStderr());
        } catch (Exception e){
            //系统异常
            e.printStackTrace();
            runResult.setStatus(Constants.Judge.STATUS_SYSTEM_ERROR.getStatus());
            runResult.setMsg("系统发生错误，请联系管理员处理！");
        }

        redisCache.setCacheObject(testJudgeReq.getUniqueKey(),runResult);

        return;

    }


    private void contestRecord(Judge judge){

        if(judge.getCid().equals(0L))
            return;
        //比赛提交，缓存纪录

        String pid = judge.getPid().toString();
        String zKey = RedisConst.RANKS + judge.getCid().toString();
        String hKey = zKey + ":"  + judge.getUserId().toString();
        //这个用户pid题的得分情况
        ContestRecordDTO contestRecordDTO = redisCache.getCacheMapValue(hKey, pid);

        //本次得分情况
        ContestRecordDTO newContestRecordDTO = new ContestRecordDTO();
        newContestRecordDTO.setJudgeId(judge.getId());
        newContestRecordDTO.setMemory(judge.getMemory());
        newContestRecordDTO.setTime(judge.getTime());
        newContestRecordDTO.setScore(judge.getScore());

        //增量
        long difference = 0;
        //没有提交过
        if(StringUtils.isNull(contestRecordDTO)){
            contestRecordDTO = newContestRecordDTO;
            difference = newContestRecordDTO.getScore();
        }else {
            //提交过，算差分
            difference = newContestRecordDTO.getScore().intValue() - contestRecordDTO.getScore().intValue();
            //这次提交是最高分
           if(difference > 0){
               contestRecordDTO =  newContestRecordDTO;
           } else if(difference == 0){
              //分相同，时间更少
               if(newContestRecordDTO.getTime() < contestRecordDTO.getTime()){
                   contestRecordDTO = newContestRecordDTO;
               }
           }
        }
        //纪录该题的最高得分判题信息
        redisCache.setCacheMapValue(hKey,pid,contestRecordDTO);

        //总分增量
        if(difference > 0)
            redisCache.incrementCacheZSet(zKey,judge.getUserId().toString(),difference);

    }
}
